#pragma once
#include "Log.hpp"
#include "INETADDR.hpp"
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <memory>
#include "Common.hpp"
using namespace std;
const int dbacklog = 8;
class Socket
{
public:
    Socket()
    {
    }
    virtual void SocketOrDie() = 0;
    virtual void BindOrDie(uint16_t port) = 0;
    virtual void ListenOrDie(int backlog) = 0;
    virtual shared_ptr<Socket> Accept(INETADDR *client) = 0;
    virtual void Close() = 0;
    virtual int Recv(string *out) = 0;
    virtual int Send(const string &message) = 0;
    virtual int Connect(string &server_ip, uint16_t port) = 0;

    void BuildTcpSocketMethod(uint16_t port, int backlog = dbacklog)
    {
        SocketOrDie();
        BindOrDie(port);
        ListenOrDie(backlog);
    }
    void BuildTcpClientSocketMethod()
    {
        SocketOrDie();
    }
    virtual ~Socket() {}
   

private:
};
class TcpSocket : public Socket
{
public:
    TcpSocket()
        : _sockfd(-1)
    {
    }
    TcpSocket(int fd)
        : _sockfd(fd)
    {
    }
    void SocketOrDie() override
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd == -1)
        {
            LOG(LogLevel::FATAL) << "socket erro";
            exit(SOCKET_ERRO);
        }
        LOG(LogLevel::INFORMATION) << "socket success";
    }
    void BindOrDie(uint16_t port) override
    {
        INETADDR server(port);
        int n = bind(_sockfd, server.NetAddrPtr(), server.NetAddrLen());
        if (n != 0)
        {
            LOG(LogLevel::FATAL) << "bind erro";
            exit(BIND_ERRO);
        }
        LOG(LogLevel::INFORMATION) << "bind success";
    }
    void ListenOrDie(int backlog) override
    {
        int n = listen(_sockfd, backlog);
        if (n != 0)
        {
            LOG(LogLevel::FATAL) << "listen erro";
            exit(LISTEN_ERRO);
        }
        LOG(LogLevel::INFORMATION) << "listen success";
    }
    shared_ptr<Socket> Accept(INETADDR *client) override
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int sock = accept(_sockfd, CONVERT(peer), &len);
        if (sock == -1 )
        {
            LOG(LogLevel::FATAL) << "accept erro";
            exit(LISTEN_ERRO);
            return nullptr;
        }
        cout << endl;
        client->SetAddr(peer);
        return make_shared<TcpSocket>(sock);
    }
    void Close() override
    {
        if (_sockfd >= 0)
            close(_sockfd);
    }
    int Recv(string *out) override
    {
        char buf[1024];
        ssize_t n = recv(_sockfd, buf, sizeof(buf) - 1, 0);
        if (n == -1)
        {
            LOG(LogLevel::WARNING) << "recv erro";
            exit(RECV_ERRO);
        }
        buf[n] = 0;
        *out += buf;
        return n;
    }
    int Send(const string &message) override
    {
        return send(_sockfd, message.c_str(), message.size(), 0);
    }
    int Connect(string &server_ip, uint16_t port) override
    {
        INETADDR server(server_ip,port);
        int n = connect(_sockfd,server.NetAddrPtr(),server.NetAddrLen());   
        if(n == -1)
        {
            LOG(LogLevel::INFORMATION) << "connect erro";
            exit(CONNECT_ERRO);
        }
        return n;
    }
    ~TcpSocket()
    {
    }

private:
    int _sockfd;
};